# -*- coding: utf-8 -*-
"""Olah Data K-NN.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1GqOfYTg0Wet3HjMw3sfoFkdBNNQV1DdS

# import library
"""

from google.colab import drive
drive.mount('/content/drive')

import pandas as pd
from sklearn.neighbors import KNeighborsClassifier

df = pd.read_excel('BIG DATA UJI GC.xlsx')
df.head(10)

"""# Klasifikasi Data

"""

df.info()
df.isnull().sum()

df['lama_pengiriman'] = (
    df['Tanggal POD'] - df['Tanggal']
).dt.days
display(df.head(10))

estimated_delivery_days = {
    'BOSSPACK': 3,
    'REGPACK': 6,
    'JAGOPACK': 8,
    'BIGPACK': 2,
}

df['estimasi_lama_pengiriman'] = df['Nama Produk'].map(estimated_delivery_days)

df['status_pengiriman'] = (
    df['lama_pengiriman'] > df['estimasi_lama_pengiriman']
).astype(int)

print("Deliveries classified as 'late' (1) or 'on time' (0).")
display(df[['Nama Produk', 'lama_pengiriman', 'estimasi_lama_pengiriman', 'status_pengiriman']].sort_values(by=['status_pengiriman', 'lama_pengiriman'], ascending=[False, False]).head(15))

display(df['status_pengiriman'].value_counts())

late_deliveries_by_product = df[df['status_pengiriman'] == 1]['Nama Produk'].value_counts()
total_late_deliveries = late_deliveries_by_product.sum()
status_by_product = df['Nama Produk'].value_counts().rename('Total').to_frame()

percentage_contribution = (late_deliveries_by_product / total_late_deliveries * 100).round(2)


contribution_df = pd.DataFrame({
    'Number of Late Deliveries': late_deliveries_by_product,
    'Total Shipments': status_by_product['Total'],
    'Percentage Contribution (%)': percentage_contribution
}).sort_values(by='Percentage Contribution (%)', ascending=False)

display(contribution_df)

"""# Normalisasi Data"""

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
fitur_normalisasi = ['estimasi_lama_pengiriman','lama_pengiriman']
df_normalisasi = scaler.fit_transform(df[fitur_normalisasi])
df_normalisasi = pd.DataFrame(df_normalisasi, columns=fitur_normalisasi)
df_normalisasi.head(10)

categorical_cols = ['Alamat Pengirim', 'Alamat Penerima', 'Kota Tujuan', 'Nama Produk', 'Status Terakhir', 'Status Terakhir']
df_encoded = pd.get_dummies(df, columns=categorical_cols, drop_first=True)
x = df_encoded.drop('status_pengiriman', axis=1)
y = df_encoded['status_pengiriman']
print("shape x:", x.shape)
print("shape y:", y.shape)

"""# Data Split"""

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, f1_score, recall_score, classification_report

x_cleaned = x.drop(columns=['Tanggal', 'Tanggal POD'])

x_train, x_test, y_train, y_test = train_test_split(x_cleaned, y, test_size=0.2, random_state=42)

error_rate_knn = []
k_value = []
pred_knn = []
acc_knn = []
k_range = range(1, 10)
for i in k_range:
    knn = KNeighborsClassifier(n_neighbors=i)
    k_value.append(i)
    knn.fit(x_train, y_train)
    pred_i = knn.predict(x_test)
    pred_knn.append(pred_i)
    error_rate_i = np.mean(pred_i != y_test)
    error_rate_knn.append(error_rate_i)
    acc_knn.append(accuracy_score(y_test, pred_i))

"""# K-NN

"""

import matplotlib.pyplot as plt

plt.figure(figsize=(7, 5))
plt.plot(k_value, error_rate_knn, color='blue', linestyle='dashed', marker='o', markerfacecolor='red', markersize=10)
plt.title('Error Rate vs. K Value')
plt.xlabel('K Value')
plt.ylabel('Error Rate')
plt.grid(True)
plt.show()

plt.figure(figsize=(7, 5))
plt.plot(k_value, acc_knn, color='green', linestyle='dashed', marker='o', markerfacecolor='purple', markersize=10)
plt.title('Accuracy vs. K Value')
plt.xlabel('K Value')
plt.ylabel('Accuracy')
plt.grid(True)
plt.show()

best_k_index = np.argmax(acc_knn)
best_k_value = k_value[best_k_index]

print(f"The best K value (based on highest accuracy) is: {best_k_value}")

"""# Cross Validation

"""

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import StratifiedKFold

k_range = range(1, 10)

mean_accuracy_scores = []
mean_precision_scores = []
mean_recall_scores = []
mean_f1_scores = []

skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

print("Evaluating K-Nearest Neighbors for different 'k' values...")

for k_neighbors in k_range:
    accuracy_list_k = []
    precision_list_k = []
    recall_list_k = []
    f1_list_k = []

    for fold, (train_index, test_index) in enumerate(skf.split(x_cleaned, y)):
        X_train_fold = x_cleaned.iloc[train_index]
        X_test_fold = x_cleaned.iloc[test_index]

        y_train_fold = y.iloc[train_index]
        y_test_fold = y.iloc[test_index]

        scaler = StandardScaler()

        X_train_scaled = scaler.fit_transform(X_train_fold)
        X_test_scaled = scaler.transform(X_test_fold)

        X_train_scaled_df = pd.DataFrame(X_train_scaled, columns=X_train_fold.columns, index=X_train_fold.index)
        X_test_scaled_df = pd.DataFrame(X_test_scaled, columns=X_test_fold.columns, index=X_test_fold.index)

        model = KNeighborsClassifier(n_neighbors=k_neighbors)
        model.fit(X_train_scaled_df, y_train_fold)
        y_pred_fold = model.predict(X_test_scaled_df)

        accuracy_list_k.append(accuracy_score(y_test_fold, y_pred_fold))

        precision_list_k.append(precision_score(y_test_fold, y_pred_fold, average='weighted', zero_division=0))
        recall_list_k.append(recall_score(y_test_fold, y_pred_fold, average='weighted', zero_division=0))
        f1_list_k.append(f1_score(y_test_fold, y_pred_fold, average='weighted', zero_division=0))

    mean_accuracy_scores.append(np.mean(accuracy_list_k))
    mean_precision_scores.append(np.mean(precision_list_k))
    mean_recall_scores.append(np.mean(recall_list_k))
    mean_f1_scores.append(np.mean(f1_list_k))

    print(f"k = {k_neighbors:2d} | Avg Accuracy: {mean_accuracy_scores[-1]:.4f} | Avg Precision: {mean_precision_scores[-1]:.4f} | Avg Recall: {mean_recall_scores[-1]:.4f} | Avg F1-Score: {mean_f1_scores[-1]:.4f}")

print("\nEvaluation Complete.")

"""# K-NN CV

"""

import matplotlib.pyplot as plt
import numpy as np

best_k_index_cv = np.argmax(mean_accuracy_scores)
best_k_value = k_range[best_k_index_cv]

print(f"The best K value (based on highest average accuracy from cross-validation) is: {best_k_value}")

mean_error_scores = 1 - np.array(mean_accuracy_scores)

plt.figure(figsize=(8, 6))
plt.plot(k_range, mean_error_scores, color='blue', linestyle='dashed', marker='o', markerfacecolor='red', markersize=8, label='Error Rate')
plt.plot(best_k_value, mean_error_scores[best_k_index_cv], color='green', marker='X', markersize=12, markeredgewidth=2, markeredgecolor='black', label=f'Best K ({best_k_value}) Error Rate')
plt.title('Average Error Rate vs. K Value (Cross-Validation)')
plt.xlabel('K Value')
plt.ylabel('Average Error Rate')
plt.grid(True)
plt.legend()
plt.show()

plt.figure(figsize=(8, 6))
plt.plot(k_range, mean_accuracy_scores, color='green', linestyle='dashed', marker='o', markerfacecolor='purple', markersize=8, label='Accuracy')
plt.plot(best_k_value, mean_accuracy_scores[best_k_index_cv], color='red', marker='X', markersize=12, markeredgewidth=2, markeredgecolor='black', label=f'Best K ({best_k_value}) Accuracy')
plt.title('Average Accuracy vs. K Value (Cross-Validation)')
plt.xlabel('K Value')
plt.ylabel('Average Accuracy')
plt.grid(True)
plt.legend()
plt.show()

"""# Confusion Matrix

"""

from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

y_pred_best_k = pred_knn[best_k_index]

cm = confusion_matrix(y_test, y_pred_best_k)

plt.figure(figsize=(6, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False,
            xticklabels=['Predicted 0', 'Predicted 1'],
            yticklabels=['Actual 0', 'Actual 1'])
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.title('Confusion Matrix for Best KNN Model (K={})'.format(best_k_value))
plt.show()

from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred_best_k, target_names=['ON time (0)', 'Delayed (1)']))

final_knn_model = KNeighborsClassifier(n_neighbors=best_k_value)
final_knn_model.fit(x_cleaned, y)

all_predictions = final_knn_model.predict(x_cleaned)

delivery_predictions_df = df[['Nama Produk']].copy()
delivery_predictions_df['Predicted_Delivery_Status'] = all_predictions

delivery_predictions_df['Predicted_Delivery_Status'] = delivery_predictions_df['Predicted_Delivery_Status'].map({0: 'On Time', 1: 'Delayed'})

print("Delivery Delay Predictions per Shipment Product:")
display(delivery_predictions_df.head(5))

print("\nSummary of Predicted Delivery Status by Product:")
display(delivery_predictions_df.groupby('Nama Produk')['Predicted_Delivery_Status'].value_counts().unstack(fill_value=0))

percentage_delay_by_product = delivery_predictions_df.groupby('Nama Produk')['Predicted_Delivery_Status'].apply(lambda x: (x == 'Delayed').sum() / len(x) * 100)

percentage_delay_by_product_df = percentage_delay_by_product.reset_index(name='Delay Percentage')
percentage_delay_by_product_df = percentage_delay_by_product_df.sort_values(by='Delay Percentage', ascending=False)

print("Delay Prediction Percentage per Shipment Product:")
display(percentage_delay_by_product_df)

actual_status_counts = df.groupby('Nama Produk')['status_pengiriman'].value_counts().unstack(fill_value=0)
actual_status_counts.rename(columns={0: 'On Time (Actual)', 1: 'Delayed (Actual)'}, inplace=True)

predicted_status_counts = delivery_predictions_df.groupby('Nama Produk')['Predicted_Delivery_Status'].value_counts().unstack(fill_value=0)
predicted_status_counts.rename(columns={'On Time': 'On Time (Predicted)', 'Delayed': 'Delayed (Predicted)'}, inplace=True)

comparison_df = actual_status_counts.join(predicted_status_counts)

comparison_df = comparison_df[['On Time (Actual)', 'Delayed (Actual)', 'On Time (Predicted)', 'Delayed (Predicted)']]

print("\n--- Actual vs. Predicted Delivery Status Comparison ---")
display(comparison_df)